#include <barsread.h>
#include <aknlists.h>
#include <aknviewappui.h>
#include <StopWatch_2005B914.rsg>
#include <aknsbasicbackgroundcontrolcontext.h>

#include "StopWatchContainer.h"
#include "StopWatchView.h"
#include "StopWatch.hrh"

namespace
{
  _LIT(KTimeFormat, "%F%H%:1%T%:2%S.%*C1");
}

CStopWatchContainer::CStopWatchContainer()
  : iTimeFormatter(KTimeFormat(), TTimeFormatter::EPrependWith0BasedNumberOfDays)
{
  iListBox = NULL;
}

CStopWatchContainer::~CStopWatchContainer()
{
  delete iListBox;
  iListBox = NULL;
  if ( iFont )
    CEikonEnv::Static()->ScreenDevice()->ReleaseFont(iFont);
  iFont = NULL;
  delete iTimeLabel;
  iTimeLabel = NULL;
  delete iBackground;
  delete iCallback;
}

CStopWatchContainer* CStopWatchContainer::NewL(const TRect& aRect, const CCoeControl* aParent,
    MEikCommandObserver* aCommandObserver, TTimeIntervalMicroSeconds aInitialValue)
{
  CStopWatchContainer* self = CStopWatchContainer::NewLC(aRect, aParent, aCommandObserver, aInitialValue);
  CleanupStack::Pop(self);
  return self;
}

CStopWatchContainer* CStopWatchContainer::NewLC(const TRect& aRect, const CCoeControl* aParent,
    MEikCommandObserver* aCommandObserver, TTimeIntervalMicroSeconds aInitialValue)
{
  CStopWatchContainer* self = new (ELeave) CStopWatchContainer();
  CleanupStack::PushL(self);
  self->ConstructL(aRect, aParent, aCommandObserver, aInitialValue);
  return self;
}

void CStopWatchContainer::ConstructL(const TRect& aRect, const CCoeControl* aParent,
    MEikCommandObserver* aCommandObserver, TTimeIntervalMicroSeconds aInitialValue)
{
  iCallback = new (ELeave) CAsyncCallBack(CActive::EPriorityStandard);

  InitComponentArrayL();
  if (aParent == NULL)
  {
    CreateWindowL();
  }
  else
  {
    SetContainerWindowL(*aParent);
  }
  iBackground = CAknsBasicBackgroundControlContext::NewL(KAknsIIDQsnBgAreaMain, Rect(), EFalse);
  iFocusControl = NULL;
  iCommandObserver = aCommandObserver;
  InitializeControlsL();

  SetDisplayValueL(aInitialValue);

  SetRect(aRect);
  ActivateL();
}

TInt CStopWatchContainer::CountComponentControls() const
{
  return (int) ELastControl;
}

CCoeControl* CStopWatchContainer::ComponentControl(TInt aIndex) const
{
  switch (aIndex)
  {
    case EListBox:
      return iListBox;
    case ETimeLabel:
      return iTimeLabel;
  }
  return NULL;
}

void CStopWatchContainer::SizeChanged()
{
  iBackground->SetRect(Rect());
  LayoutControls();
  CCoeControl::SizeChanged();
}

void CStopWatchContainer::LayoutControls()
{
  TRect rect = Rect();

  TRAP_IGNORE(AdjustLabelFontL(Min(rect.Width(), rect.Height())));
  TInt height = 2*iTimeLabel->Font()->HeightInPixels();

  iTimeLabel->SetExtent(TPoint(0, 0), TSize(rect.Width(), height));
  iListBox->SetExtent(TPoint(0, height), TSize(rect.Width(), rect.Height() - height));
//  iTimeLabel->iMargin.iTop = iTimeLabel->iMargin.iBottom = height/2;
}

void CStopWatchContainer::SetLabelTextColorL()
{
  TRgb themeTextColor;
  AknsUtils::GetCachedColor(AknsUtils::SkinInstance(), themeTextColor, KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG9);
  iTimeLabel->OverrideColorL(EColorLabelText, themeTextColor);
}

void CStopWatchContainer::AdjustLabelFontL(TInt aWidth = 0)
{
  AdjustLabelFontL(aWidth ? aWidth : iTimeLabel->Rect().Width(),  iTimeLabel->Text() ? iTimeLabel->Text()->Length() : 0);
}

void CStopWatchContainer::AdjustLabelFontL(TInt aWidth, TInt aNumberofVisibleChars)
{
  const CFont* font = AknLayoutUtils::FontFromId(EAknLogicalFontDigitalFont);
  TFontSpec fontSpec = font->FontSpecInTwips();

  if ( aNumberofVisibleChars != 0 )
  {
    TReal maxCharWidth = aWidth/TReal(aNumberofVisibleChars);
    TReal ratio = maxCharWidth/font->CharWidthInPixels(TChar('0'));
    fontSpec.iHeight *= ratio;
  }
//  fontSpec.iHeight *= 2;
  fontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);

  if ( iFont )
    CEikonEnv::Static()->ScreenDevice()->ReleaseFont(iFont);
  iFont = NULL;

  TInt err = CEikonEnv::Static()->ScreenDevice()->GetNearestFontInTwips(iFont, fontSpec);
  User::LeaveIfError(err);

  iTimeLabel->SetFont(iFont);
  DrawDeferred();
}

TKeyResponse CStopWatchContainer::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
{
  if (aKeyEvent.iCode == EKeyLeftArrow || aKeyEvent.iCode == EKeyRightArrow)
  {
    // Listbox takes all events even if it doesn't use them
    return EKeyWasNotConsumed;
  }

  if (iFocusControl != NULL && iFocusControl->OfferKeyEventL(aKeyEvent, aType) == EKeyWasConsumed)
  {
    return EKeyWasConsumed;
  }

  return CCoeControl::OfferKeyEventL(aKeyEvent, aType);
}

void CStopWatchContainer::InitializeControlsL()
{
  iTimeLabel = new (ELeave) CEikLabel;
  iTimeLabel->SetContainerWindowL(*this);
  iTimeLabel->SetBufferReserveLengthL(200); // nice long buffer

  iTimeLabel->SetLabelAlignment(ELayoutAlignCenter);
  iTimeLabel->SetAlignment(EHCenterVCenter);
  iTimeLabel->SetAllMarginsTo(0);

  SetLabelTextColorL();

  iListBox = new (ELeave) CAknSingleHeadingStyleListBox;
  iListBox->SetContainerWindowL(*this);
  {
    TResourceReader reader;
    iEikonEnv->CreateResourceReaderLC(reader, R_STOPWATCH_LIST_BOX);
    iListBox->ConstructFromResourceL(reader);
    CleanupStack::PopAndDestroy(); // reader internal state
  }
  iListBox->View()->SetListEmptyTextL(KNullDesC());
  // the listbox owns the items in the list and will free them
  iListBox->Model()->SetOwnershipType(ELbmOwnsItemArray);
  CEikScrollBarFrame* scrollbar = iListBox->CreateScrollBarFrameL(ETrue);
  scrollbar->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto);
  scrollbar->SetAdjustsVerticalModel(ETrue);

  iListBox->SetFocus(ETrue);
  iFocusControl = iListBox;
}

void CStopWatchContainer::HandleResourceChange(TInt aType)
{
  CCoeControl::HandleResourceChange(aType);
  if ( aType == KEikDynamicLayoutVariantSwitch )
  {
    TRect rect = iAvkonViewAppUi->View(TUid::Uid(EStopWatchViewId))->ClientRect();
    SetExtent(rect.iTl, rect.Size());
  }
  if ( aType == KEikMessageColorSchemeChange )
  {
    SetLabelTextColorL();
  }
}

void CStopWatchContainer::Draw(const TRect& aRect) const
{
  CWindowGc& gc = SystemGc();
  gc.Clear(aRect);
  MAknsSkinInstance* skin = AknsUtils::SkinInstance();
  MAknsControlContext* cc = AknsDrawUtils::ControlContext(this);
  AknsDrawUtils::Background(skin, cc, this, gc, aRect);
}

void CStopWatchContainer::CreateListBoxItemL(TDes& aBuffer, TInt aItemId, const TDesC& aTime1Text)
{
  _LIT ( KStringHeader, "%d\t%S\t\t" );
  aBuffer.Format(KStringHeader(), aItemId, &aTime1Text);
}

void CStopWatchContainer::SetDisplayValueL(const TTimeIntervalMicroSeconds& aValue)
{
  if ( !iTimeLabel )
    User::Leave(KErrNotReady);

  TInt prevTextLength = iTimeLabel->Text() ? iTimeLabel->Text()->Length() : 0;
  TPtrC newText = iTimeFormatter.FormatAsTimeL(aValue);
  iTimeLabel->SetTextL(newText);

  if ( newText.Length() != prevTextLength )
  {
    SizeChanged();
  }

  iTimeLabel->DrawDeferred();
}

void CStopWatchContainer::AddTimeSnapshotL(const TTimeIntervalMicroSeconds& aSnapshot)
{
  CTextListBoxModel* model = iListBox->Model();

  TBuf<200> listboxItemBuf;
  CreateListBoxItemL(listboxItemBuf, model->NumberOfItems()+1, iTimeFormatter.FormatAsTimeL(aSnapshot));

  CDesCArray* itemArray = static_cast<CDesCArray*> (model->ItemTextArray());
  itemArray->InsertL(0, listboxItemBuf);
  iListBox->ScrollToMakeItemVisible(0);
  iListBox->HandleItemAdditionL();
}

void CStopWatchContainer::SetSnapshotsL(const TArray<TTimeIntervalMicroSeconds>& aSnapshots)
{
  ResetTimeSnapshots();

  TBuf<200> listboxItemBuf;

  CTextListBoxModel* model = iListBox->Model();
  CDesCArray* itemArray = static_cast<CDesCArray*> (model->ItemTextArray());
  for (TInt i = 0; i < aSnapshots.Count(); ++i)
  {
    CreateListBoxItemL(listboxItemBuf, i + 1, iTimeFormatter.FormatAsTimeL(aSnapshots[i]));
    itemArray->InsertL(0, listboxItemBuf);
  }
  iListBox->ScrollToMakeItemVisible(0);
  iListBox->HandleItemAdditionL();  
}

void CStopWatchContainer::ResetTimeSnapshots()
{
  CTextListBoxModel* model = iListBox->Model();
  CDesCArray* itemArray = static_cast<CDesCArray*> (model->ItemTextArray());
  const TInt count = itemArray->Count();
  if ( count > 0 )
  {
    itemArray->Delete(0, count);
    iListBox->HandleItemRemovalL();
  }
}

TTypeUid::Ptr CStopWatchContainer::MopSupplyObject(TTypeUid aId)
{
  return iBackground != NULL ? MAknsControlContext::SupplyMopObject(aId, iBackground) : CCoeControl::MopSupplyObject(aId);
}

void CStopWatchContainer::ScheduleChangeFontSize()
{
  if ( iCallback->IsActive() )
    return;

  iCallback->Set(TCallBack(CStopWatchContainer::ChangeFontSizeCb, this));
  iCallback->CallBack();
}

TInt CStopWatchContainer::ChangeFontSizeCb(TAny* aThis)
{
  TRAP_IGNORE(reinterpret_cast<CStopWatchContainer*>(aThis)->ChangeFontSizeL());
  return 0;
}

void CStopWatchContainer::ChangeFontSizeL()
{
  AdjustLabelFontL();
  SizeChanged();
}


